// Copyright (c) 2022 Presto Labs Pte. Ltd.
// Author: sungraecho

#include <unordered_map>

#include <cxxopts.hpp>
#include <glog/logging.h>

#include "coin2/strategy/linear_model/product_strategy.h"
#include "coin2/app/driver.h"
#include "coin2/base/config/app_config.h"
#include "coin2/base/config/args.h"
#include "coin2/base/time.h"
#include "coin2/exchange/feed.pb.h"
#include "coin2/strategy/strategy.h"

#include "presto/quant/symbology/symbol.h"
#include "presto/quant/symbology/symbol_manager.h"
#include "presto/quant/matrix/calculator.h"

using ::presto::quant::matrix::Calculator;
using ::coin2::exchange::base::feed::FeedUpdate;
using ::coin2::strategy::linear_model::FeatureFeedConverter;
using ::presto::Symbol;

namespace interval_feature {

using namespace coin2::app;
using namespace coin2::base::config;

struct IntervalFeatureConfig {
  int resolution{};
};

class IntervalFeatureStrategy : public ::coin2::strategy::IStrategy {
 public:
  explicit IntervalFeatureStrategy(const IntervalFeatureConfig& config,
      const std::experimental::filesystem::path& calculator_spec_filename)
    : config_(config),
      mm(calculator_spec_filename) {
    converter.reset(new FeatureFeedConverter(
        mm.GetSymbolManager(),
        &product_cache,
        true));
  }

  ~IntervalFeatureStrategy() {
    LOG(INFO) << "Dump hdf";
    mm.Write();
    LOG(INFO) << "Bye";
  }

  void onBookFeed(const FeedUpdate& upd) override {
    converter->onBookFeed(upd);
    if (converter->feed_converted) {
      const Symbol* symbol = converter->GetSymbol(upd);
      if (symbol == nullptr) return;
      mm.HandleBook(symbol, converter->last_feed_msg);
    }
  }
  void onTradeFeed(const FeedUpdate& upd) override {
    converter->onTradeFeed(upd);
    if (converter->feed_converted) {
      const Symbol* symbol = converter->GetSymbol(upd);
      if (symbol == nullptr) return;
      mm.HandleTrade(symbol, converter->last_feed_msg);
    }
  }
 private:
  IntervalFeatureConfig config_;
  std::unique_ptr<FeatureFeedConverter> converter;
  Calculator mm;
  ::coin2::exchange::base::symbology::ProductEncyclopedia product_cache;
};

}  // namespace interval_feed

int main(int argc, char* argv[]) {
  coin2::app::DriverConfig app;
  cxxopts::Options opt("Interval Feature", "test strategy");
  opt.add_options()("calculator_spec_filename", "", cxxopts::value<std::string>());

  coin2::base::config::AddDriverOptions(&opt);
  coin2::base::config::AddIntervalOptions(&opt);

  auto res = opt.parse(argc, argv);

  coin2::base::config::ParseDriverOptions(res, &app);
  coin2::base::config::ParseIntervalOptions(
      res,
      app.mutable_feed()->mutable_common()->mutable_archive()->mutable_interval());

  LOG(INFO) << app.DebugString();

  ::coin2::base::InitLogging(argv[0], app.log());

  interval_feature::IntervalFeatureConfig config;
  interval_feature::IntervalFeatureStrategy strategy(config,
      res["calculator_spec_filename"].as<std::string>());
  ::coin2::app::Driver driver(app, &strategy);
  driver.Run();

  // LOG(INFO) << "Finish reading feed";
  // // strategy.Write();

  return 0;
}
